<?php
namespace hangpu8\hpcms\service;

use Exception;
use think\facade\Config;
use think\facade\Db;

class CrudService
{
    // 表名称
    private $table = '';
    // 表注释
    private $remark = '';
    // 应用名称
    private $appModule = '';
    // 父类名称
    private $extendName = '';
    // 应用版本
    private $version = '';
    // 应用根目录
    private $rootPath = '';

    /**
     * 构造函数
     *
     * @param string $table
     * @param string $appModule
     * @param string $remark
     * @param string $extendName
     * @param string $version
     */
    public function __construct(string $table,string $appModule,string $remark,string $extendName,string $version)
    {
        // 获得基础信息
        $this->table = ucfirst($table);
        $this->remark = $remark;
        $this->appModule = $appModule;
        $this->extendName = empty($extendName) ? '' : ucfirst($extendName);
        $this->version = empty($version) ? '' : $version;

        // 获得类包根目录
        $dirPath = dirname(__DIR__);
        // 将反斜杠转成正斜杠，兼容windows与linux多端平台
        $dirPath = str_replace("\\","/",$dirPath);
        $rootPath = str_replace("\\","/",dirname(dirname(dirname(dirname($dirPath)))));
        // 将路径赋值给类属性
        $this->HpCmsRootPath = $dirPath;
        // 获得应用路径
        $this->rootPath = $rootPath;
    }

    /**
     * 创建数据表
     *
     * @param array $tableList
     * @return boolean
     */
    public function createTables(array $tableList) : bool
    {
        // 字段为空时不创建表
        if (empty($tableList))
        {
            return true;
        }
        $table_name = uncamelize($this->table);
        $tableRemark = $this->remark;
        $driver = Config::get('database.default');
        $table_prefix = Config::get('database.connections.' . $driver . '.prefix');
        $table_name = "{$table_prefix}{$table_name}";
        $sql = "SHOW TABLES LIKE '{$table_name}'";
        if (Db::query($sql)) {
            throw new Exception('数据表已存在');
        }
        // 开始创建数据库
        Db::startTrans();
        try {
            $table_data = '';
            $parimary_key = '';
            foreach ($tableList as $item)
            {
                // 字段名称与字段类型
                $fieldStr = "`{$item['field']}` {$item['type']}";
                // 字段长度
                if (empty($item['length']))
                {
                    switch ($item['type'])
                    {
                        case 'int':
                            $fieldStr .= "(11)";
                        break;
                        case 'varchar':
                            $fieldStr .= "(255)";
                        break;
                        default:
                            $fieldStr .= "(0)";
                        break;
                    }
                }else{
                    $fieldStr .= "({$item['length']})";
                }
                // 是否主键
                if ($item['primary'] == true)
                {
                    $parimary_key .= $item['field'];
                    $fieldStr .=" NOT NULL";
                }
                else if($item['default'] == 'NOT NULL'){
                    $fieldStr .=" NOT NULL";
                }else if(!$item['default']){
                    switch ($item['type'])
                    {
                        case 'varchar':
                            $fieldStr .= " DEFAULT ''";
                        break;
                        default:
                            $fieldStr .= " NULL";
                        break;
                    }
                }else{
                    switch ($item['type'])
                    {
                        case 'int':
                            $fieldStr .= " NULL";
                        break;
                        case 'decimal' || 'double' || 'float':
                            $fieldStr .= " NULL";
                        break;
                        case 'timestamp' || 'time' || 'datetime':
                            $fieldStr .= " NULL";
                        break;
                        default:
                            $fieldStr .= " DEFAULT '{$item['default']}'";
                        break;
                    }
                }
                // 是否自增
                if ($item['inc'] == true)
                {
                    $fieldStr .=" AUTO_INCREMENT";
                }
                // 表注释
                $fieldStr.=" COMMENT '".$item['remarks']."',";
                // 加入组
                $table_data .= $fieldStr;
            }
            $sql = "CREATE TABLE IF NOT EXISTS `$table_name` (
            $table_data
            PRIMARY KEY (`$parimary_key`)
            ) ENGINE=innoDB DEFAULT CHARSET=utf8 COMMENT='{$tableRemark}' AUTO_INCREMENT=1;";
            Db::query($sql);
            // 提交事务
            Db::commit();
            return true;
        } catch (\Exception $e) {
            // 回滚事务
            Db::rollback();
            throw new Exception($e->getMessage());
        }
    }

    /**
     * 创建控制器
     *
     * @param array $crud
     * @return boolean
     */
    public function createController(array $crud) : bool
    {
        // 获得继承与版本号
        $extend = $this->extendName ? " extends {$this->extendName}" : '';
        $version = $this->version ? "\\{$this->version}" : "";
        $versionDir = $this->version ? "{$this->version}/" : "";
        // 检测目标文件是否存在
        $filePath = "{$this->rootPath}/app/{$this->appModule}/controller/{$versionDir}/{$this->table}.php";
        if (file_exists($filePath))
        {
            throw new Exception("{$this->table} 控制器已存在");
        }
        // 模板文件路径
        $controllerTplPath = "{$this->HpCmsRootPath}/tpl/controller.tpl";
        // 引入控制器模板文件
        $controllerTpl = file_get_contents($controllerTplPath);
        // 替换控制器内容
        $useing = $this->useing();
        $crudCls = new \hangpu8\hpcms\service\CrudClas($this->table,$this->appModule,$this->remark);
        $actionStr = '';
        foreach ($crud as $value)
        {
            switch ($value['name'])
            {
                case 'add':
                    $actionStr .= $crudCls->addAction($value);
                break;
                case 'del':
                    $actionStr .= $crudCls->delAction($value);
                break;
                case 'edit':
                    $actionStr .= $crudCls->editAction($value);
                break;
                case 'list':
                    $actionStr .= $crudCls->listAction($value);
                break;
                case 'detail':
                    $actionStr .= $crudCls->infoAction($value);
                break;
            }
        }
        $str1 = ["{namespace}","{class}","{extend}","{version}","{remarks}","{useing}","{action}"];
        $str2 = [$this->appModule,$this->table,$extend,$version,$this->remark,$useing,$actionStr];
        $controllerContent = str_replace($str1,$str2,$controllerTpl);
        // 写入控制器内容，并返回写入内容
        return $this->createFile($filePath,$controllerContent);
    }

    /**
     * 创建数据模型
     *
     * @return boolean
     */
    public function createModel() : bool
    {
        // 检测目标文件是否存在
        $filePath = "{$this->rootPath}/app/{$this->appModule}/model/{$this->table}.php";
        if (file_exists($filePath))
        {
            throw new Exception("{$this->table} 数据模型已存在");
        }
        // 引入模型模板文件
        $modelTplPath = "{$this->HpCmsRootPath}/tpl/model.tpl";
        // 读取模型文件数据
        $modelContent = file_get_contents($modelTplPath);
        // 替换模型内容数据
        $str1 = ['{namespace}','{class}'];
        $str2 = [$this->appModule,$this->table];
        $modelContent = str_replace($str1,$str2,$modelContent);
        // 写入控制器内容，并返回写入内容
        return $this->createFile($filePath,$modelContent);
    }

    /**
     * 创建验证模型
     *
     * @param array $validate
     * @return boolean
     */
    public function createValidateModel(array $validate) : bool
    {
        // 检测目标文件是否存在
        $filePath = "{$this->rootPath}/app/{$this->appModule}/validate/{$this->table}.php";
        if (file_exists($filePath))
        {
            throw new Exception("{$this->table} 验证模型已存在");
        }
        // 引入模板文件
        $validateTplPath = "{$this->HpCmsRootPath}/tpl/validate.tpl";
        // 读取模型文件数据
        $validateContent = file_get_contents($validateTplPath);
        $ruleStr = '';
        $messageStr = '';
        $crudCls = new \hangpu8\hpcms\service\CrudValidate;
        $ruleList = $crudCls->parseRule($validate);
        $ruleStr .= $crudCls->createRule($ruleList);
        $messageStr .= $crudCls->createMessage($ruleList);
        // 替换逻辑层内容
        $str1 = ['{namespace}','{class}','{rule}','{message}'];
        $str2 = [$this->appModule,$this->table,$ruleStr,$messageStr];
        $validateContent = str_replace($str1,$str2,$validateContent);
        // 写入控制器内容，并返回写入内容
        return $this->createFile($filePath,$validateContent);
    }

    /**
     * 引入命名空间
     *
     * @return void
     */
    private function useing()
    {
        $className = ucfirst($this->table);
        $useing = "use \\hangpu8\\hpcms\\Utils\\Json;\n";
        $useing .= "use Exception;\n";
        $useing .= "use hg\\apidoc\\annotation as Apidoc;\n";
        $useing .= "use app\\{$this->appModule}\\model\\{$className} as {$this->table}Model;";
        return $useing;
    }

    /**
     * 保存文件
     * @param $path
     * @param $str_tmp
     * @return bool
     */
    public function createFile(string $path, string $str_tmp): bool
    {
        $pathArr = explode("/", $path);
        unset($pathArr[count($pathArr) - 1]);
        $dir = implode("/", $pathArr);
        if (!file_exists($dir)) {
            mkdir($dir, 0777, true);
        }
        $fp = fopen($path, "w") or die("Unable to open file!");
        fwrite($fp, $str_tmp); //存入内容
        fclose($fp);
        return true;
    }
}